package cn.com.chengmandian.bike.service.impl;

import cn.com.chengmandian.bike.model.*;
import cn.com.chengmandian.bike.mapper.EbRideCommandMapper;
import cn.com.chengmandian.bike.service.*;
import cn.com.chengmandian.core.common.constants.CommonConstant;
import cn.com.chengmandian.core.redis.cache.AgentParamCacheService;
import cn.com.chengmandian.core.redis.cache.BikeSuperviseCacheService;
import cn.com.chengmandian.core.redis.service.RedisService;
import cn.com.chengmandian.core.web.exceptions.BadResponseException;
import cn.com.chengmandian.core.web.pojo.AjaxResult;
import cn.com.chengmandian.core.web.pojo.BikeAreaParam;
import cn.com.chengmandian.rabbitmq.event.BikeEventCode;
import cn.com.chengmandian.rabbitmq.event.EventService;
import cn.com.chengmandian.service.bike.BikeCenterClient;
import cn.com.chengmandian.service.bike.IotBikeClient;
import cn.com.chengmandian.service.bike.constants.*;
import cn.com.chengmandian.service.bike.form.SendBikeCommandForm;
import cn.com.chengmandian.service.bike.pojo.CommandResponse;
import cn.com.chengmandian.service.bike.pojo.Gps;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;

/**
 * <p>
 * 电单车运营订单指令 服务实现类
 * </p>
 *
 * @author AutoGenerator
 * @since 2021-12-11
 */
@Slf4j
@Service
public class EbRideCommandServiceImpl extends ServiceImpl<EbRideCommandMapper, EbRideCommand> implements IEbRideCommandService {
    @Autowired
    private IotBikeClient iotBikeClient;

    @Autowired
    private IEbRideOrderService rideOrderService;

    @Autowired
    private IEbRentOrderService rentOrderService;

    @Autowired
    private IEbDeviceBikeService deviceBikeService;

    @Autowired
    private AgentParamCacheService agentParamCacheService;

    @Autowired
    private BikeCenterClient bikeCenterClient;

    @Autowired
    private BikeSuperviseCacheService superviseCacheService;

    @Autowired
    private IEbCommonParamService paramService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private IEbRideDistanceService rideDistanceService;

    @Autowired
    private EventService eventService;

    @Autowired
    private IEbRideHelmetService rideHelmetService;

    @Override
    public EbRideCommand addCommand(EbRideOrder rideOrder, DeviceCommandEnum command, Integer userType, Integer userId, String userName) {
        return this.addCommand(rideOrder.getAgentId(), rideOrder.getAreaId(), rideOrder.getId(),rideOrder.getBikeId(), rideOrder.getBikeNo(), command, userType, userId, userName, RideCommandTypeEnum.RIDE, null);
    }

    @Override
    public EbRideCommand addCommand(EbRentOrder rentOrder, DeviceCommandEnum command, Integer userType, Integer userId, String userName, Date startTime) {
        return this.addCommand(rentOrder.getAgentId(), rentOrder.getAreaId(), rentOrder.getId(), rentOrder.getBikeId(), rentOrder.getBikeNo(), command, userType, userId, userName, RideCommandTypeEnum.RENT, startTime);
    }


    public EbRideCommand addCommand(Integer agentId, Integer areaId,Integer orderId, Integer bikeId, String bikeNo, DeviceCommandEnum command, Integer userType, Integer userId, String userName, RideCommandTypeEnum commandTypeEnum, Date startTime){
        // 统一参数配置
        BikeAreaParam bikeAreaParam = agentParamCacheService.getParam(agentId, areaId);

        // 失败之前初始化指令
        this.update(new LambdaUpdateWrapper<EbRideCommand>().set(EbRideCommand::getStatus, RideCommandStatusEnum.FAIL.getCode())
                .set(EbRideCommand::getEndTime, new Date())
                .set(EbRideCommand::getFailReason, "新指令下单，作废旧指令")
                .eq(EbRideCommand::getType, commandTypeEnum.getCode())
                .eq(EbRideCommand::getOrderId, orderId)
                .eq(EbRideCommand::getStatus, RideCommandStatusEnum.INIT.getCode()));

        // 新建指令
        EbRideCommand rideCommand = new EbRideCommand();
        rideCommand.setOrderId(orderId);
        rideCommand.setType(commandTypeEnum.getCode());
        rideCommand.setCommand(command.getType());
        rideCommand.setStatus(RideCommandStatusEnum.INIT.getCode());
        rideCommand.setBikeId(bikeId);
        rideCommand.setStartTime(startTime);
        this.save(rideCommand); // 必须先保存数据，否则有几率数据还没建立，就会收到响应
        AjaxResult<CommandResponse> res = null;
        SendBikeCommandForm commandForm = new SendBikeCommandForm()
                .setAgentId(agentId)
                .setBikeNo(bikeNo)
                .setUserId(userId)
                .setUserName(userName)
                .setUserType(userType)
                .setIsSync(CommonConstant.ONE);
        if (command == DeviceCommandEnum.OPEN_LOCK) {
            commandForm.setCommand(DeviceCommandEnum.OPEN_LOCK.getType());
            if(bikeAreaParam != null && bikeAreaParam.getUserOpenLockCheckHelmetResponse()){
                JSONObject  postData = new JSONObject();
                postData.put("checkHelmetWear", 1);
                commandForm.setPostData(postData.toJSONString());
            }
            res = iotBikeClient.sendBikeCommand(commandForm);
        } else if (command == DeviceCommandEnum.STOP_LOCK) {
            commandForm.setCommand(DeviceCommandEnum.STOP_LOCK.getType());
            res = iotBikeClient.sendBikeCommand(commandForm);
        } else if (command == DeviceCommandEnum.CLOSE_LOCK) {
            commandForm.setCommand(DeviceCommandEnum.CLOSE_LOCK.getType());
            res = iotBikeClient.sendBikeCommand(commandForm);
        }
        if (res == null) {
            this.failCommand(rideCommand.getId(), "无效的指令");
            throw new BadResponseException("操作失败：不支持的操作命令");
        } else {
            this.update(new LambdaUpdateWrapper<EbRideCommand>()
                    .set(EbRideCommand::getMsgId, res.getData() != null ? res.getData().getMsgId() : null)
                    .eq(EbRideCommand::getId, rideCommand.getId()));
        }
        rideCommand.setMsgId(res.getData() != null ? res.getData().getMsgId() : null);
        if (!res.isSuccess()) {
            this.failCommand(rideCommand.getId(), res.getMessage());
            if (command == DeviceCommandEnum.OPEN_LOCK && commandTypeEnum.getCode().equals(RideCommandTypeEnum.RIDE.getCode())) {
                rideOrderService.failInitOrder(orderId);
                // 如果是锁车
            }
            throw new BadResponseException(res.getCode(), res.getMessage());
        }
        return rideCommand;
    }

    /**
     * 订单开锁，可能是临时锁车开锁
     */
    @Override
    public void orderOpenCommand(EbDeviceBike deviceBike, EbRideOrder rideOrder, EbRideCommand rideCommand, String remark) {
        String key = "CMD:RIDE:ORDER:COMMAND:OPEN:" + deviceBike.getAgentId() +  ":" + deviceBike.getId() + ":" + rideCommand.getId();
        if(redisService.hasKey(key)) {
            log.error("orderOpenCommand 已通知开锁成功，本次放弃 bikeNo={},orderNo={},commandId={}", deviceBike.getBikeNo(), rideOrder.getOrderNo(), rideCommand.getId());
            return;
        }
        redisService.set(key, true, 5);

        // 开始骑行
        rideOrderService.update(new LambdaUpdateWrapper<EbRideOrder>()
                .set(EbRideOrder::getStatus, RideOrderStatusEnum.RIDING.getCode())
                .set(rideOrder.getOpenTime() == null, EbRideOrder::getOpenTime, new Date())
                .eq(EbRideOrder::getId, rideOrder.getId()));

        if(rideOrder.getStatus().equals(RideOrderStatusEnum.INIT.getCode())) {
            // 如果是闲置车
            boolean isIdeaBike = deviceBike.getStatus().equals(BikeStatusEnum.WAIT_DISPATCH_STATUS.getCode())
                && deviceBike.getStatusReason() != null
                && deviceBike.getStatusReason().equals(BikeStatusReasonEnum.IDLE_TO_DISPATCH.getCode());

            // 检查配置是否要求佩戴头盔 生成骑行订单头盔记录
            BikeAreaParam bikeAreaParam = agentParamCacheService.getParam(rideOrder.getAgentId(), rideOrder.getAreaId());
            if(bikeAreaParam != null && bikeAreaParam.getUserCloseLockCheckHelmetReignStatus()) {
                rideHelmetService.createRecord(rideOrder.getId(), deviceBike);
            }

            // 生成骑行订单距离信息
            rideDistanceService.addDistance(rideOrder.getId(), deviceBike.getId(), deviceBike.getLng(), deviceBike.getLat());

            // 更新车辆使用状态
            deviceBikeService.update(new LambdaUpdateWrapper<EbDeviceBike>()
                    .set(isIdeaBike, EbDeviceBike::getStatus, BikeStatusEnum.USE_STATUS.getCode())
                    .set(isIdeaBike, EbDeviceBike::getStatusReason, null)
                    .set(EbDeviceBike::getUseStatus, DeviceBikeUseStatusEnum.RIDE.getCode())
                    .set(EbDeviceBike::getUsePerson, CommonConstant.ONE)
                    .set(EbDeviceBike::getLastRideTime, new Date())
                    .set(EbDeviceBike::getLeaveAreaTime, null) // 重置下，防止有问题
                    .set(deviceBike.getStartStaticTime() != null, EbDeviceBike::getStartStaticTime, new Date()) // 重置静止时间，方便算订单车辆静止时间
                    .set(EbDeviceBike::getRideTimes, deviceBike.getRideTimes() + 1)
                    .eq(EbDeviceBike::getId, rideOrder.getBikeId()));
        } else {
            // 如果有一条暂停指令正在执行，则结束
            this.endCommand(this.getCommand(rideOrder.getId(), DeviceCommandEnum.STOP_LOCK, RideCommandStatusEnum.START, RideCommandTypeEnum.RIDE));
        }

        // 开始骑行指令
        this.startCommand(rideCommand.getId(), remark);

        // 发送RIDE_ORDER_LOCK_OPEN_SUCCESS事件
        if(rideOrder.getStatus().equals(RideOrderStatusEnum.INIT.getCode())) {
            JSONObject eventData = new JSONObject();
            eventData.put("bikeId", rideOrder.getBikeId());
            eventData.put("orderId", rideOrder.getId());
            eventData.put("agentId", rideOrder.getAgentId());
            eventData.put("areaId", rideOrder.getAreaId());
            eventData.put("memberId", rideOrder.getMemberId());
            eventService.sendEvent(BikeEventCode.RIDE_ORDER_LOCK_OPEN_SUCCESS,
                eventData,
                "USER_rideCommandService_orderOpenCommand");
        }

        try {
            // 下发头盔锁指令
            if(!deviceBike.getHelmetInstall().equals(BikeHelmetInstallEnum.NO.getCode())){
                BikeAreaParam bikeAreaParam = agentParamCacheService.getParam(rideOrder.getAgentId(), rideOrder.getAreaId());
                if(bikeAreaParam != null && bikeAreaParam.getUserNeedWearHelmet() && bikeAreaParam.getUserOpenLockOpenHelmet()){
                    SendBikeCommandForm commandForm = new SendBikeCommandForm()
                            .setAgentId(deviceBike.getAgentId())
                            .setBikeNo(deviceBike.getBikeNo())
                            .setCommand(DeviceCommandEnum.OPEN_HELMET.getType())
                            .setUserId(0)
                            .setUserName("借车自动解锁头盔")
                            .setUserType(UserTypeEnum.SYS.getCode());
                    AjaxResult<CommandResponse> res = iotBikeClient.sendBikeCommand(commandForm);
                    if (!res.isSuccess()) {
                        log.error("借车自动解锁头盔失败：{}, {}", deviceBike.getBikeNo(), res.getMessage());
                    }
                }
            }
        } catch (Exception e) {
            log.error("iotBikeClient.sendBikeCommand error", e);
        }
    }

    /**
     * 订单临时停车
     */
    @Override
    @Transactional
    public void orderStopCommand(EbDeviceBike deviceBike, EbRideOrder rideOrder, EbRideCommand stopInitCommand, Integer closeUserType) {
        rideOrderService.update(new LambdaUpdateWrapper<EbRideOrder>()
                .set(EbRideOrder::getStatus, RideOrderStatusEnum.PAUSE.getCode())
                .set(EbRideOrder::getStopTime, new Date())
                .eq(EbRideOrder::getId, rideOrder.getId()));

        // 推送临时停车指令
        this.orderStopCommandPush(rideOrder, closeUserType);

        // 如果有一条骑行指令正在执行，则结束
        this.endCommand(this.getCommand(rideOrder.getId(), DeviceCommandEnum.OPEN_LOCK, RideCommandStatusEnum.START, RideCommandTypeEnum.RIDE));

        // 开始暂停指令
        this.startCommand(stopInitCommand.getId(), null);
    }

    // 临时停车成功消息推送
    @Override
    public void orderStopCommandPush(EbRideOrder rideOrder, Integer closeUserType) {
        try {
            JSONObject eventData = new JSONObject();
            eventData.put("bikeId", rideOrder.getBikeId());
            eventData.put("orderId", rideOrder.getId());
            eventData.put("agentId", rideOrder.getAgentId());
            eventData.put("areaId", rideOrder.getAreaId());
            eventData.put("memberId", rideOrder.getMemberId());
            eventData.put("closeUserType", closeUserType);
            eventService.sendEvent(BikeEventCode.RIDE_ORDER_STOP_SUCCESS,
                    eventData,
                    "USER_rideCommandService_orderBikeStopSuccess");
        } catch (Exception e) {
            log.error("BikeEventCode RIDE_ORDER_STOP_SUCCESS rabbitmq error:{}", e.getMessage());
        }
    }

    /**
     * 收到结束订单关锁成功指令通知
     */
    @Override
    public void orderCloseCommand(EbDeviceBike deviceBike, EbRideOrder rideOrder, EbRideCommand closeInitCommand, String remark, Integer userType) {
        // 切换系统的时候，没有结算的订单
        if (rideOrder.getCloseTime() == null) {
            rideOrderService.orderSettleForClose(rideOrder, null, UserTypeEnum.MEMBER.getCode());
        }
        rideOrderService.orderBikeReturn(rideOrder.getId(), false, userType);
        rideOrderService.confirmAutoPay(rideOrder.getId(), userType);

        // 如果有一条骑行指令正在执行，则结束
        this.endCommand(this.getCommand(rideOrder.getId(), DeviceCommandEnum.OPEN_LOCK, RideCommandStatusEnum.START, RideCommandTypeEnum.RIDE));

        // 如果有一条暂停指令正在执行，则结束
        this.endCommand(this.getCommand(rideOrder.getId(), DeviceCommandEnum.STOP_LOCK, RideCommandStatusEnum.START, RideCommandTypeEnum.RIDE));

        // 开始结束指令
        this.startCommand(closeInitCommand.getId(), remark);

        this.endCommand(closeInitCommand);
    }

    @Override
    public void orderCloseCommand(EbDeviceBike deviceBike, EbRentOrder rentOrder, EbRideCommand closeInitCommand) {
        // 结束订单
        rentOrderService.rentOrderCloseLock(rentOrder, deviceBike);

        // 如果有一条骑行指令正在执行，则结束
        this.endCommand(this.getCommand(rentOrder.getId(), DeviceCommandEnum.OPEN_LOCK, RideCommandStatusEnum.START, RideCommandTypeEnum.RENT));

        // 如果有一条暂停指令正在执行，则结束
        this.endCommand(this.getCommand(rentOrder.getId(), DeviceCommandEnum.STOP_LOCK, RideCommandStatusEnum.START, RideCommandTypeEnum.RENT));
    }

    @Override
    public Integer getCommandCount(Integer orderId, DeviceCommandEnum command) {
        return (int)this.count(new LambdaQueryWrapper<EbRideCommand>()
                .eq(EbRideCommand::getOrderId, orderId)
                .eq(EbRideCommand::getCommand, command.getType()));
    }

    /**
     * 获取指令
     */
    @Override
    public EbRideCommand getCommand(Integer orderId, Integer command, RideCommandStatusEnum status, RideCommandTypeEnum commandTypeEnum) {
        return this.getOne(new LambdaQueryWrapper<EbRideCommand>()
                .eq(EbRideCommand::getOrderId, orderId)
                .eq(EbRideCommand::getCommand, command)
                .eq(EbRideCommand::getType, commandTypeEnum.getCode())
                .eq(EbRideCommand::getStatus, status.getCode())
                .orderByDesc(EbRideCommand::getId)
                .last("limit 1"));
    }

    /**
     * 获取指令
     */
    @Override
    public EbRideCommand getCommand(Integer orderId, DeviceCommandEnum command, RideCommandStatusEnum status, RideCommandTypeEnum commandTypeEnum) {
        return this.getOne(new LambdaQueryWrapper<EbRideCommand>()
                .eq(EbRideCommand::getOrderId, orderId)
                .eq(EbRideCommand::getCommand, command.getType())
                .eq(EbRideCommand::getType, commandTypeEnum.getCode())
                .eq(EbRideCommand::getStatus, status.getCode())
                .orderByDesc(EbRideCommand::getId)
                .last("limit 1"));
    }

    /**
     * 开始指令
     */
    public void startCommand(Integer id, String failReason) {
        Date now = new Date();
        this.update(new LambdaUpdateWrapper<EbRideCommand>()
                .set(EbRideCommand::getStatus, RideCommandStatusEnum.START.getCode())
                .set(EbRideCommand::getStartTime, now)
                .set(EbRideCommand:: getFailReason, failReason)
                .eq(EbRideCommand::getId, id));
    }

    /**
     * 结束指令
     */
    public void endCommand(EbRideCommand rideCommand) {
        if (rideCommand == null) return;
        Date endTime = new Date();
        this.update(new LambdaUpdateWrapper<EbRideCommand>()
                .set(EbRideCommand::getStatus, RideCommandStatusEnum.END.getCode())
                .set(EbRideCommand::getEndTime, endTime)
                .set(EbRideCommand::getDifTime, DateUtil.between(rideCommand.getStartTime() == null ? new Date() : rideCommand.getStartTime(), endTime, DateUnit.SECOND))
                .eq(EbRideCommand::getId, rideCommand.getId()));
    }

    /**
     * 失败指令
     */
    @Override
    public void failCommand(Integer id, String failReason) {
        this.update(new LambdaUpdateWrapper<EbRideCommand>()
                .set(EbRideCommand::getStatus, RideCommandStatusEnum.FAIL.getCode())
                .set(EbRideCommand::getFailReason, failReason)
                .eq(EbRideCommand::getId, id));
        log.error("指令执行失败：{}", failReason);
    }
}
